package com.genreate.util.linuxUtil;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.StrUtil;
import com.genreate.constant.CmdCommandConstant;
import com.genreate.constant.EnvironmentPathConstant;
import com.jcraft.jsch.*;

import java.io.*;
import java.net.Socket;
import java.util.List;
import java.util.Map;

public class LinuxUtil {
    public static void main(String[] args) {
        String command = CmdCommandConstant.gitPullCommand("D:\\ideaProjects\\lys\\gzFile", "123");
        String commandCheckout = CmdCommandConstant.gitCheckoutCommand("D:\\ideaProjects\\lys\\gzFile", "123");
        EnvironmentMap environmentMap = new EnvironmentMap("MVN_HONE", EnvironmentPathConstant.MAVEN_PATH);
        cmdCommand(command, null);
        cmdCommand(commandCheckout, null);

    }

    /**
     * maven打jar包
     *
     * @param path 路径
     */
    public static void mavenPackage(String path) {
        String command = CmdCommandConstant.mavenPackageCommand(path);
        EnvironmentMap environmentMap = new EnvironmentMap("MVN_HONE", EnvironmentPathConstant.MAVEN_PATH);
        cmdCommand(command, environmentMap);
    }

    /**
     * maven打指定环境的jar包
     *
     * @param path        路径
     * @param profileName 配置文件名称
     */
    public static void mavenPackage(String path, String profileName) {
        String command = CmdCommandConstant.mavenPackageProfileCommand(path, profileName);
        EnvironmentMap environmentMap = new EnvironmentMap("MVN_HONE", EnvironmentPathConstant.MAVEN_PATH);
        cmdCommand(command, environmentMap);
    }

    /**
     * 获取命令列表 按换行符 分割
     *
     * @param command 命令
     * @return {@link List }<{@link String }>
     */
    public static List<String> getCommandList(String command) {
        if (StrUtil.isBlank(command)) {
            throw new RuntimeException("命令不能为空");
        }
        return StrUtil.split(command, "\n");
    }

    /**
     * 执行cmd命令
     *
     * @param command        命令
     * @param environmentMap 环境变量
     */
    public static int cmdCommand(String command, EnvironmentMap environmentMap) {
        List<String> commandList = getCommandList(command);
        return cmdCommand(commandList, environmentMap);
    }

    /**
     * 执行cmd命令
     *
     * @param commandList    命令列表
     * @param environmentMap 环境变量
     */
    public static int cmdCommand(List<String> commandList, EnvironmentMap environmentMap) {
        if (IterUtil.isEmpty(commandList)) {
            throw new RuntimeException("命令不能为空");
        }

        // 要执行的命令
        ProcessBuilder processBuilder = new ProcessBuilder(commandList);

        // 获取当前环境变量
        Map<String, String> env = processBuilder.environment();
        if (environmentMap != null) {
            // 设置 环境变量
            env.put(environmentMap.getEnvName(), environmentMap.getPath());
            //加入到PATH中
            env.put("PATH", StrUtil.format("{}\\bin;{}", environmentMap.getPath(), env.get("PATH")));
        }

        try {
            // 启动进程
            Process process = processBuilder.start();

            // 获取命令执行的输出流
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                // 将输出打印到控制台
                System.out.println(line);
            }

            // 获取命令执行的错误流
            BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            while ((line = errorReader.readLine()) != null) {
                // 将错误信息打印到控制台
                System.err.println(line);
            }

            // 等待命令执行完成，并获取退出状态码
            int exitCode = process.waitFor();
            System.out.println("执行完毕 " + exitCode);
            return exitCode;

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return -999;
    }

    /**
     * 端口是否打开
     *
     * @param ip      ip
     * @param port    港口
     * @param timeout 超时 毫秒
     * @return boolean
     */
    public static boolean isPortOpen(String ip, int port, int timeout) {
        try (Socket socket = new Socket()) {
            socket.connect(new java.net.InetSocketAddress(ip, port), timeout);
            // 连接成功，端口开放
            return true;
        } catch (IOException e) {
            // 连接失败，端口关闭或不可达
            return false;
        }
    }

    /**
     * 端口是否打开
     *
     * @param ip   ip
     * @param port 港口
     * @return boolean
     */
    public static boolean isPortOpen(String ip, int port) {
        return isPortOpen(ip, port, 2000);
    }

    /**
     * 连接linux服务器
     *
     * @param serverIp 服务器ip
     * @param port     港口
     * @param username 用户名
     * @param password 密码
     * @return 会话
     */
    public static Session createSession(String serverIp, int port, String username, String password) {
        Session session = null;
        try {
            // 创建 JSch 对象并设置会话
            JSch jsch = new JSch();
            session = jsch.getSession(username, serverIp, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            System.out.println("连接到服务器成功！");
        } catch (Exception e) {
            System.out.println("连接到服务器失败！");
            e.printStackTrace();
        }
        return session;
    }

    /**
     * 创建会话
     *
     * @param serverIp     服务器ip
     * @param port         港口
     * @param username     用户名
     * @param password     密码
     * @param rootPassword root密码
     * @return {@link Session }
     */
    public static Session createSession(String serverIp, int port, String username, String password,
                                        String rootPassword) {
        Session session = null;
        Channel channel = null;
        try {
            // 创建 JSch 对象并设置会话（使用 test 用户登录）
            JSch jsch = new JSch();
            session = jsch.getSession(username, serverIp, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();

            // 打开一个 shell 通道
            channel = session.openChannel("shell");
            channel.setInputStream(null);

            // 获取输入流和输出流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            channel.setOutputStream(baos);
            channel.setExtOutputStream(baos);

            // 启动通道
            channel.connect();

            // 获取输出流用于发送命令
            OutputStream os = channel.getOutputStream();

            // 发送 su 命令切换到 root
            os.write(("su -\n").getBytes());
            os.flush();

            // 等待一段时间，让命令执行
            Thread.sleep(2000);

            // 发送 root 密码
            os.write((rootPassword + "\n").getBytes());
            os.flush();

            // 验证是否成功切换到 root
            Thread.sleep(1000);
            String output = baos.toString();
            System.out.println("切换用户输出：\n" + output);

            if (output.contains("Password:") && !output.contains("#")) {
                System.err.println("切换到 root 用户失败！");
                closeResources(session, channel);
                return null;
            }

            System.out.println("已成功切换到 root 用户！");
            return session;

        } catch (Exception e) {
            System.err.println("连接或切换用户失败！");
            e.printStackTrace();
            closeResources(session, channel);
            return null;
        }
    }

    /**
     * 关闭资源
     *
     * @param session 会话
     * @param channel 频道
     */
    private static void closeResources(Session session, Channel channel) {
        try {
            if (channel != null && channel.isConnected()) {
                channel.disconnect();
            }
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 复制文件 执行命令
     *
     * @param localFilePath  本地文件路径
     * @param remoteFilePath 远程文件路径
     * @param command        命令
     */
    public static void copyFileExecuteCommand(Session session, String localFilePath, String remoteFilePath, String command) {
        ChannelSftp sftpChannel = null;

        try {
            // 打开 SFTP 通道并上传文件
            sftpChannel = (ChannelSftp) session.openChannel("sftp");
            sftpChannel.connect();
            System.out.println("SFTP 通道打开成功！");

            // 复制文件并显示进度
            System.out.println("开始复制文件...");
            SftpProgressMonitor monitor = new MyProgressMonitor();
            sftpChannel.put(localFilePath, remoteFilePath, monitor, ChannelSftp.OVERWRITE);
            System.out.println("文件复制完成！");

            // 执行 Docker 重启命令
            executeCommand(session, command);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sftpChannel != null && sftpChannel.isConnected()) {
                sftpChannel.disconnect();
            }
        }
    }

    /**
     * 执行命令
     *
     * @param session 会话
     * @param command 命令
     * @throws JSchException        jsch异常
     * @throws IOException          ioexception
     * @throws InterruptedException 中断异常
     */
    public static void executeCommand(Session session, String command) {
        System.out.println(StrUtil.format("正在执行命令:{}", command));
        try {
            ChannelExec execChannel = (ChannelExec) session.openChannel("exec");
            execChannel.setCommand(command);
            execChannel.setInputStream(null);
            execChannel.setErrStream(System.err);

            InputStream execInput = execChannel.getInputStream();
            execChannel.connect();

            // 读取命令执行结果
            byte[] tmp = new byte[1024];
            while (true) {
                while (execInput.available() > 0) {
                    int i = execInput.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    System.out.print(new String(tmp, 0, i));
                }
                if (execChannel.isClosed()) {
                    System.out.println("Exit status: " + execChannel.getExitStatus());
                    break;
                }
                Thread.sleep(1000);
            }
            execChannel.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 1.git拉取最新代码
     * 2.git切换到当前分支
     * 3.打指定环境的包
     *
     * @param projectPath 项目路径
     * @param branchName  分支名称
     */
    public static void gitPackage(String projectPath, String branchName, String profileName) {
        //拉取最新代码
        String command = CmdCommandConstant.gitPullCommand(projectPath, branchName);
        cmdCommand(command, null);
        System.out.println("*******************拉取最新代码完毕**********************");

        //切换到当前分支
        String commandCheckout = CmdCommandConstant.gitCheckoutCommand(projectPath, branchName);
        cmdCommand(commandCheckout, null);
        System.out.println("*******************切换到当前分支完毕**********************");

        //打指定环境的包
        mavenPackage(projectPath, profileName);
        System.out.println("*******************打包完毕**********************");
    }

    /**
     * 将某个文件夹下的文件夹 压缩成 zpi，并移动到另外一个文件夹下
     *
     * @param session              会话
     * @param fileFloderPathPrefix 要压缩的文件夹路径 前缀
     * @param fileFloderName       要压缩的文件夹名称
     * @param backFloderPath       要存放备份文件的路径
     * @param zipFileName          压缩包的名字
     */
    public static void createZipMv(Session session, String fileFloderPathPrefix, String fileFloderName, String backFloderPath, String zipFileName) {
        //创建备份文件夹，如果没有则创建，已存在不会修改
        String mkdirCommand = StrUtil.format("mkdir -p {}", backFloderPath);
        LinuxUtil.executeCommand(session, mkdirCommand);
        //压缩文件夹命令
        String zipFileFloderCommand = StrUtil.format("zip -r {}/{} {}/{}", fileFloderPathPrefix, zipFileName, fileFloderPathPrefix, fileFloderName);
        LinuxUtil.executeCommand(session, zipFileFloderCommand);
        //移动压缩包命令
        String mvZipFileFloderCommand = StrUtil.format("mv  {}/{} {}", fileFloderPathPrefix, zipFileName, backFloderPath);
        LinuxUtil.executeCommand(session, mvZipFileFloderCommand);
    }
}